// service/impl/UserServiceImpl.java
package org.example.clouddemo.service.common.impl;

import cn.hutool.core.lang.Snowflake;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.clouddemo.bean.common.entity.User;
import org.example.clouddemo.bean.common.param.LoginParam;
import org.example.clouddemo.bean.common.param.RegisterParam;
import org.example.clouddemo.bean.common.param.UpdateUserInfoParam;
import org.example.clouddemo.bean.common.vo.LoginResultVo;
import org.example.clouddemo.bean.common.vo.UserAppVo;
import org.example.clouddemo.util.JwtUtil;
import org.example.clouddemo.service.common.UserService;
import org.example.clouddemo.service.common.mapper.UserMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Slf4j // 添加日志
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final PasswordEncoder passwordEncoder;
    private final JwtUtil jwtUtil;
    private final RedisTemplate<String, Object> redisTemplate; // 注入RedisTemplate

    // Redis中存储用户信息的Key前缀
    private static final String LOGIN_USER_KEY_PREFIX = "login:user:";
    private final Snowflake snowflake;
    private final UserMapper userMapper;


    @Override
    public String register(RegisterParam registerParam) {
        // 1. 检查用户名是否已存在
        // 使用 ServiceImpl 提供的方法 this.count()
        long count = this.count(new QueryWrapper<User>().eq("username", registerParam.getNickname()));
        if (count > 0) {
            throw new RuntimeException("用户名: '" + registerParam.getNickname() + "' 已被注册");
        }

        // 2. 创建用户实体
        User user = new User();
        //user.setUsername(registerParam.getUsername());
        user.setNickname(registerParam.getNickname());

        // 3. 使用注入的 PasswordEncoder 对密码进行加密
        user.setPassword(passwordEncoder.encode(registerParam.getPassword()));

        // 设置创建和更新时间
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());

        //user的id用雪花算法自增
        user.setId(snowflake.nextId());

        // user的图像地址
        user.setAvatarUrl(String.valueOf(registerParam.getAvatarChoose())+".png");


        // 4. 保存到数据库
        // 使用 ServiceImpl 提供的方法 this.save()
        this.save(user);

        User user1 = userMapper.selectById(user.getId());

        return String.valueOf(user1.getUsername());
    }

    @Override
    public LoginResultVo login(LoginParam loginParam) {
        // 1. 根据用户名查询用户
        //User user = this.getOne(new QueryWrapper<User>().eq("username", loginParam.getUsername()).eq("enable_flag", 1));
        User user = this.getOne(new QueryWrapper<User>().eq("username", loginParam.getUsername()));

        if (user == null) {
            throw new RuntimeException("用户名或密码错误");
        }

        // 2. 验证密码
        if (!passwordEncoder.matches(loginParam.getPassword(), user.getPassword())) {
            throw new RuntimeException("用户名或密码错误");
        }

        // 3. 登录成功, 生成唯一的UUID作为Redis key和JWT的载荷
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");

        // 4. 生成JWT
        String token = jwtUtil.generateToken(uuid);

        // 5. 将用户信息存入Redis，Key为UUID，并设置与JWT相同的过期时间
        String redisKey = LOGIN_USER_KEY_PREFIX + uuid;
        // 注意：这里存储的是完整的user对象，实际项目中建议存储一个更轻量的VO/DTO对象
        redisTemplate.opsForValue().set(redisKey, user, jwtUtil.getExpiration(), TimeUnit.SECONDS);

        // 6. 封装并返回结果
        LoginResultVo loginResultVo = new LoginResultVo();
        loginResultVo.setToken(token);

        return loginResultVo;
    }

    @Override
    public void logout(String token) {
        // 1. 从token中解析出UUID
        // 如果token本身是 "Bearer xxx" 格式，需要先去除 "Bearer "
        if (StringUtils.hasText(token) && token.startsWith("Bearer ")) {
            token = token.substring(7);
        }

        String uuid = jwtUtil.getUuidFromToken(token);

        // 2. 如果UUID存在，则从Redis中删除对应的Key
        if (StringUtils.hasText(uuid)) {
            String redisKey = LOGIN_USER_KEY_PREFIX + uuid;
            Boolean deleteResult = redisTemplate.delete(redisKey);
            log.info("用户退出登录，删除Redis Key: {}, 结果: {}", redisKey, deleteResult);
        } else {
            log.warn("尝试退出登录失败，无效的Token");
        }
    }

    // 在 UserServiceImpl 类中，添加下面这个新方法的完整实现：

    @Override
    public User getUserInfoByToken(String token) {
        // 1. 清理 token 前缀 "Bearer "
        if (StringUtils.hasText(token) && token.startsWith("Bearer ")) {
            token = token.substring(7);
        }

        // 2. 从 token 中解析出 UUID
        String uuid = jwtUtil.getUuidFromToken(token);
        if (!StringUtils.hasText(uuid)) {
            // 如果无法解析出UUID，说明token无效或已过期
            throw new RuntimeException("无效的Token，请重新登录");
        }

        // 3. 使用 UUID 从 Redis 中获取用户信息
        String redisKey = LOGIN_USER_KEY_PREFIX + uuid;
        User user = (User) redisTemplate.opsForValue().get(redisKey);

        if (user == null) {
            // 如果Redis中没有，说明用户已退出或登录信息已过期
            throw new RuntimeException("登录已过期，请重新登录");
        }

        // 4. 返回从Redis中获取到的用户信息
        // 注意：这里的User对象可能包含密码等敏感信息，在实际生产中
        // 应该返回一个不含敏感信息的DTO/VO对象。为简化演示，我们直接返回User。
        return user;
    }


    @Override
    public void updateUserInfo(String token, UpdateUserInfoParam param) {
        // 1. 从token中解析用户信息
        if (StringUtils.hasText(token) && token.startsWith("Bearer ")) {
            token = token.substring(7);
        }
        String uuid = jwtUtil.getUuidFromToken(token);

        // 2. 从 Redis 中获取当前用户，并检查用户是否存在
        String redisKey = LOGIN_USER_KEY_PREFIX + uuid;
        User cachedUser = (User) redisTemplate.opsForValue().get(redisKey);

        if (cachedUser == null) {
            // 如果Redis中没有，说明用户已退出或登录信息已过期
            throw new RuntimeException("登录已过期，请重新登录");
        }
        Long userId = cachedUser.getId();


        // 3. 构建需要更新的User实体 (后续逻辑保持不变)
        User userToUpdate = new User();
        userToUpdate.setId(userId);
        boolean needUpdate = false;

        // 3.1 处理昵称更新
        if (StringUtils.hasText(param.getNickname())) {
            userToUpdate.setNickname(param.getNickname());
            needUpdate = true;
        }

        // 3.2 处理头像更新
        if (StringUtils.hasText(param.getAvatarChoose())) {
            String avatarUrl = "http://your-cdn.com/avatars/" + param.getAvatarChoose() + ".png"; // 请替换为您的URL
            userToUpdate.setAvatarUrl(avatarUrl);
            userToUpdate.setAvatarUrl(param.getAvatarChoose()+".jpg");
            needUpdate = true;
        }

        // 4. 如果没有任何需要更新的字段，则直接返回
        if (!needUpdate) {
            return;
        }

        // 5. 执行数据库更新
        userToUpdate.setUpdateTime(LocalDateTime.now());
        int rows = userMapper.updateById(userToUpdate);
        if (rows <= 0) {
            throw new RuntimeException("更新用户信息失败，请稍后重试");
        }

        // 6. 关键步骤：同步更新Redis缓存
        Long expire = redisTemplate.getExpire(redisKey, TimeUnit.SECONDS);
        if (expire != null && expire > 0) {
            // 从数据库重新查询完整的、最新的用户信息
            User updatedUser = userMapper.selectById(userId);

            // !!重要!!: 在存入Redis前，确保敏感信息（如密码）已被清除
            updatedUser.setPassword(null);

            // 将最新的User对象写回Redis，并设置回原来的过期时间
            redisTemplate.opsForValue().set(redisKey, updatedUser, expire, TimeUnit.SECONDS);
        }
    }


    /**
     * 【新增】getInfo方法的实现
     */
    @Override
    public UserAppVo getInfo(Long userId) {
        // 1. 根据用户ID从数据库查询完整的用户信息
        // 使用 ServiceImpl 自带的 getById 方法
        User user = this.getById(userId);

        if (user == null) {
            // 在正常业务流程中，能走到这里说明userId是有效的，
            // 但以防万一，比如用户被后台删除了，还是要做个判断。
            throw new RuntimeException("用户不存在或已被删除");
        }

        // 2. 创建用于前端展示的VO对象
        UserAppVo userAppVo = new UserAppVo();

        // 3. 将User实体中的属性拷贝到UserAppVo中
        // BeanUtils.copyProperties 会自动匹配同名同类型的属性进行赋值
        BeanUtils.copyProperties(user, userAppVo);

        // 4. 返回脱敏后的VO对象（UserAppVo中不应包含密码等敏感字段）
        return userAppVo;
    }


}
